﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;

namespace System.Windows.Media.DirectShow.Filters
{
    public class VideoAudioRenderer : BaseFilter, IBasicAudio, IBasicVideo, IUIElement, IReferenceClock
    {

        private MediaElement mediaElement;
        private AudioRendererPin audioRendererPin;
        private VideoRendererPin videoRendererPin;

        private DirectShowMediaStreamSource mediaStreamSource;

        public VideoAudioRenderer()
        {
            mediaElement = new MediaElement();
            mediaElement.Volume = 1;
            mediaElement.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(mediaElement_MediaFailed);

            audioRendererPin = new AudioRendererPin(this);
            videoRendererPin = new VideoRendererPin(this);

            this.Pins.Add(audioRendererPin);
            this.Pins.Add(videoRendererPin);
        }

        void mediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
        {

        }

        #region IBasicAudio Members

        public double Balance
        {
            get
            {
                return this.mediaElement.Balance;
            }
            set
            {
                this.mediaElement.Balance = value;
            }
        }

        public double Volume
        {
            get
            {
                return this.mediaElement.Volume;
            }
            set
            {
                this.mediaElement.Volume = value;
            }
        }

        #endregion

        #region IBasicVideo Members

        public Size VideoSize
        {
            get { return new Size(mediaElement.NaturalVideoWidth, mediaElement.NaturalVideoHeight); }
        }

        #endregion

        #region IUIElement Members

        public UIElement UIElement
        {
            get { return mediaElement; }
        }

        #endregion

        public override string Name
        {
            get { return "Video & Audio Renderer"; }
        }

        protected override void StopInternal()
        {
            base.StopInternal();

            this.Graph.Dispatcher.BeginInvoke(() =>
            {
                StopTimers();

                try
                {
                    this.mediaElement.Stop();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                }
            });
        }

        private void StopTimers()
        {
            foreach (DispatcherTimer timer in this.timers.Keys)
                timer.Stop();
        }

        protected override void PauseInternal()
        {
            base.PauseInternal();

            this.Graph.Dispatcher.BeginInvoke(() =>
            {
                StopTimers();

                this.mediaElement.Pause();
            });
        }

        protected override void SetGraph(IFilterGraph graph)
        {
            base.SetGraph(graph);
        }

        protected override void RunInternal(TimeSpan start)
        {
            ManualResetEvent e = new ManualResetEvent(false);
            this.Graph.Dispatcher.BeginInvoke(delegate
            {
                if (this.mediaElement.CurrentState == MediaElementState.Stopped ||
                    this.mediaElement.CurrentState == MediaElementState.Closed)
                {
                    //this.mediaElement.Source = null;
                    mediaStreamSource = new DirectShowMediaStreamSource(mediaElement);
                    mediaStreamSource.AudioRenderPin = audioRendererPin;
                    mediaStreamSource.VideoRenderPin = videoRendererPin;
                    audioRendererPin.SetMediaStreamSource(mediaStreamSource);
                    videoRendererPin.SetMediaStreamSource(mediaStreamSource);
                    this.mediaElement.SetSource(this.mediaStreamSource);
                }

                if (this.mediaElement.CurrentState != MediaElementState.Playing)
                    this.mediaElement.Play();

                e.Set();

                // Reschedule timer
                foreach (KeyValuePair<DispatcherTimer, TimeSpan> timer in this.timers)
                {
                    PrepareTimer(timer.Key, timer.Value);
                    timer.Key.Start();
                }
            });
            e.WaitOne();

            base.RunInternal(start);
        }


        #region IReferenceClock Members

        TimeSpan IReferenceClock.GetTime()
        {
            return this.mediaElement.Position;
        }

        private Dictionary<DispatcherTimer, TimeSpan> timers = new Dictionary<DispatcherTimer, TimeSpan>();

        void IReferenceClock.AdviseTime(TimeSpan time, TimerCallback handler, object state)
        {
            this.Graph.Dispatcher.BeginInvoke(() =>
            {
                DispatcherTimer timer = new DispatcherTimer();
                timers.Add(timer, time);
                PrepareTimer(timer, time);
                timer.Tick += delegate
                {
                    timers.Remove(timer);
                    timer.Stop();
                    handler(state);
                };
                timer.Start();
            });
        }

        private void PrepareTimer(DispatcherTimer timer, TimeSpan time)
        {
            TimeSpan due = time - ((IReferenceClock)this).GetTime();
            if (due.Ticks < 0)
                due = TimeSpan.Zero;

            //Debug.WriteLine("due {0}", due);
            timer.Interval = due;
        }

        #endregion
    }
}
